﻿using System;
using System.Collections.Generic;

using System.Text;

namespace RoughJs.Ast
{
    /// <summary>
    /// 2010-01-5 ----- 2010-01-29
    /// 陈曦 1.0
    /// Microsoft Public License (Ms-PL)
    ///This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
    ///1. Definitions
    ///The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
    ///A "contribution" is the original software, or any additions or changes to the software.
    ///A "contributor" is any person that distributes its contribution under this license.
    ///"Licensed patents" are a contributor's patent claims that read directly on its contribution.
    ///2. Grant of Rights
    ///(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
    ///(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
    ///3. Conditions and Limitations
    ///(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
    ///(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
    ///(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
    ///(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
    ///(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 
    /// </summary>
    public class IRFactory
    {
        public IRFactory(Parser parser)
        {
            this.parser = parser;
        }
        /// <summary>
        /// Leaf 叶
        /// </summary>
        /// <param name="nodeType"></param>
        /// <returns></returns>
        public Node createLeaf(int nodeType)
        {
            return new Node(nodeType);
        }


        /**
     * Script (for associating file/url names with toplevel scripts.)
     */
        public void initScript(ScriptOrFnNode scriptNode, Node body)
        {
            Node children = body.getFirstChild();
            if (children != null) { scriptNode.addChildrenToBack(children); }
        }

        /// <summary>
        /// 创建脚本节点
        /// </summary>
        /// <returns></returns>
        public ScriptOrFnNode createScript()
        {
            return new ScriptOrFnNode(Token.SCRIPT);
        }
        /// <summary>
        /// 在给出的节点后面加入结点 
        /// This function breaks the Factory abstraction, but it removes a requirement
        /// from implementors of Node.
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="child"></param>
        public void addChildToBack(Node parent, Node child)
        {
            parent.addChildToBack(child);
        }
        /// <summary>
        /// 创建二进制节点
        /// </summary>
        /// <param name="nodeType"></param>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public Node createBinary(int nodeType, Node left, Node right)
        {
            switch (nodeType)
            {

                case Token.ADD:
                    // numerical addition and string concatenation
                    if (left.type == Token.STRING)
                    {
                        String s2;
                        if (right.type == Token.STRING)
                        {
                            s2 = right.getString();
                        }
                        else if (right.type == Token.NUMBER)
                        {
                            s2 = ScriptRuntime.numberToString(right.getDouble(), 10);
                        }
                        else
                        {
                            break;
                        }
                        String s1 = left.getString();
                        left.setString(s1 + s2);
                        return left;
                    }
                    else if (left.type == Token.NUMBER)
                    {
                        if (right.type == Token.NUMBER)
                        {
                            left.setDouble(left.getDouble() + right.getDouble());
                            return left;
                        }
                        else if (right.type == Token.STRING)
                        {
                            String s1, s2;
                            s1 = ScriptRuntime.numberToString(left.getDouble(), 10);
                            s2 = right.getString();
                            right.setString(s1 + s2);
                            return right;
                        }
                    }
                    // can't do anything if we don't know  both types - since
                    // 0 + object is supposed to call toString on the object and do
                    // string concantenation rather than addition
                    break;

                case Token.SUB:
                    // numerical subtraction
                    if (left.type == Token.NUMBER)
                    {
                        double ld = left.getDouble();
                        if (right.type == Token.NUMBER)
                        {
                            //both numbers
                            left.setDouble(ld - right.getDouble());
                            return left;
                        }
                        else if (ld == 0.0)
                        {
                            // first 0: 0-x -> -x
                            return new Node(Token.NEG, right);
                        }
                    }
                    else if (right.type == Token.NUMBER)
                    {
                        if (right.getDouble() == 0.0)
                        {
                            //second 0: x - 0 -> +x
                            // can not make simply x because x - 0 must be number
                            return new Node(Token.POS, left);
                        }
                    }
                    break;

                case Token.MUL:
                    // numerical multiplication
                    if (left.type == Token.NUMBER)
                    {
                        double ld = left.getDouble();
                        if (right.type == Token.NUMBER)
                        {
                            //both numbers
                            left.setDouble(ld * right.getDouble());
                            return left;
                        }
                        else if (ld == 1.0)
                        {
                            // first 1: 1 *  x -> +x
                            return new Node(Token.POS, right);
                        }
                    }
                    else if (right.type == Token.NUMBER)
                    {
                        if (right.getDouble() == 1.0)
                        {
                            //second 1: x * 1 -> +x
                            // can not make simply x because x - 0 must be number
                            return new Node(Token.POS, left);
                        }
                    }
                    // can't do x*0: Infinity * 0 gives NaN, not 0
                    break;

                case Token.DIV:
                    // number division
                    if (right.type == Token.NUMBER)
                    {
                        double rd = right.getDouble();
                        if (left.type == Token.NUMBER)
                        {
                            // both constants -- just divide, trust Java to handle x/0
                            left.setDouble(left.getDouble() / rd);
                            return left;
                        }
                        else if (rd == 1.0)
                        {
                            // second 1: x/1 -> +x
                            // not simply x to force number convertion
                            return new Node(Token.POS, left);
                        }
                    }
                    break;

                case Token.AND:
                    {
                        // Since x && y gives x, not false, when Boolean(x) is false,
                        // and y, not Boolean(y), when Boolean(x) is true, x && y
                        // can only be simplified if x is defined. See bug 309957.

                        int leftStatus = isAlwaysDefinedBoolean(left);
                        if (leftStatus == ALWAYS_FALSE_BOOLEAN)
                        {
                            // if the first one is false, just return it
                            return left;
                        }
                        else if (leftStatus == ALWAYS_TRUE_BOOLEAN)
                        {
                            // if first is true, set to second
                            return right;
                        }
                        break;
                    }

                case Token.OR:
                    {
                        // Since x || y gives x, not true, when Boolean(x) is true,
                        // and y, not Boolean(y), when Boolean(x) is false, x || y
                        // can only be simplified if x is defined. See bug 309957.

                        int leftStatus = isAlwaysDefinedBoolean(left);
                        if (leftStatus == ALWAYS_TRUE_BOOLEAN)
                        {
                            // if the first one is true, just return it
                            return left;
                        }
                        else if (leftStatus == ALWAYS_FALSE_BOOLEAN)
                        {
                            // if first is false, set to second
                            return right;
                        }
                        break;
                    }
            }

            return new Node(nodeType, left, right);
        }

        // Check if Node always mean true or false in boolean context
        private static int isAlwaysDefinedBoolean(Node node)
        {
            switch (node.getType())
            {
                case Token.FALSE:
                case Token.NULL:
                    return ALWAYS_FALSE_BOOLEAN;
                case Token.TRUE:
                    return ALWAYS_TRUE_BOOLEAN;
                case Token.NUMBER:
                    {
                        double num = node.getDouble();
                        if (num == num && num != 0.0)
                        {
                            return ALWAYS_TRUE_BOOLEAN;
                        }
                        else
                        {
                            return ALWAYS_FALSE_BOOLEAN;
                        }
                    }
            }
            return 0;
        }

        /// <summary>
        /// 创建变量
        /// </summary>
        /// <param name="token"></param>
        /// <param name="lineno"></param>
        /// <returns></returns>
        public Node createVaribles(int token, int lineno)
        {
            return new Node(token, lineno);
        }

        /// <summary>
        /// Return
        /// </summary>
        /// <param name="expr"></param>
        /// <param name="lineno"></param>
        /// <returns></returns>
        public Node createReturn(Node expr, int lineno)
        {
            return expr == null
                ? new Node(Token.RETURN, lineno)
                : new Node(Token.RETURN, expr, lineno);
        }

        public Node createYield(Node child, int lineno)
        {
            if (!parser.insideFunction())
            {
                parser.reportError("msg.bad.yield");
            }
            setRequiresActivation();
            setIsGenerator();
            if (child != null)
                return new Node(Token.YIELD, child, lineno);
            else
                return new Node(Token.YIELD, lineno);
        }

        private void setRequiresActivation()
        {
            if (parser.insideFunction())
            {
                ((FunctionNode)parser.currentScriptOrFn).itsNeedsActivation = true;
            }
        }
        private void setIsGenerator()
        {
            if (parser.insideFunction())
            {
                ((FunctionNode)parser.currentScriptOrFn).itsIsGenerator = true;
            }
        }

        public Node createUnary(int nodeType, Node child)
        {
            int childType = child.getType();
            switch (nodeType)
            {
                case Token.DELPROP:
                    {
                        Node n;
                        if (childType == Token.NAME)
                        {
                            // Transform Delete(Name "a")
                            //  to Delete(Bind("a"), String("a"))
                            child.setType(Token.BINDNAME);
                            Node left = child;
                            Node right = Node.newString(child.getString());
                            n = new Node(nodeType, left, right);
                        }
                        else if (childType == Token.GETPROP ||
                                   childType == Token.GETELEM)
                        {
                            Node left = child.getFirstChild();
                            Node right = child.getLastChild();
                            child.removeChild(left);
                            child.removeChild(right);
                            n = new Node(nodeType, left, right);
                        }
                        else if (childType == Token.GET_REF)
                        {
                            Node ref1 = child.getFirstChild();
                            child.removeChild(ref1);
                            n = new Node(Token.DEL_REF, ref1);
                        }
                        else
                        {
                            n = new Node(Token.TRUE);
                        }
                        return n;
                    }
                case Token.TYPEOF:
                    if (childType == Token.NAME)
                    {
                        child.setType(Token.TYPEOFNAME);
                        return child;
                    }
                    break;
                case Token.BITNOT:
                    if (childType == Token.NUMBER)
                    {
                        int value = ScriptRuntime.toInt32(child.getDouble());
                        child.setDouble(~value);
                        return child;
                    }
                    break;
                case Token.NEG:
                    if (childType == Token.NUMBER)
                    {
                        child.setDouble(-child.getDouble());
                        return child;
                    }
                    break;
                case Token.NOT:
                    {
                        int status = isAlwaysDefinedBoolean(child);
                        if (status != 0)
                        {
                            int type;
                            if (status == ALWAYS_TRUE_BOOLEAN)
                            {
                                type = Token.FALSE;
                            }
                            else
                            {
                                type = Token.TRUE;
                            }
                            if (childType == Token.TRUE || childType == Token.FALSE)
                            {
                                child.setType(type);
                                return child;
                            }
                            return new Node(type);
                        }
                        break;
                    }
            }
            return new Node(nodeType, child);
        }

        private Node makeReference(Node node)
        {
            int type = node.getType();
            switch (type)
            {
                case Token.NAME:
                case Token.GETPROP:
                case Token.GETELEM:
                case Token.GET_REF:
                    return node;
                case Token.CALL:
                    node.setType(Token.REF_CALL);
                    return new Node(Token.GET_REF, node);
            }
            // Signal caller to report error
            return null;
        }

        public Node createIncDec(int nodeType, bool post, Node child)
        {
            child = makeReference(child);
            if (child == null)
            {
                String msg;
                if (nodeType == Token.DEC)
                {
                    msg = "msg.bad.decr";
                }
                else
                {
                    msg = "msg.bad.incr";
                }
                parser.reportError(msg);
                return null;
            }

            int childType = child.getType();

            switch (childType)
            {
                case Token.NAME:
                case Token.GETPROP:
                case Token.GETELEM:
                case Token.GET_REF:
                    {
                        Node n = new Node(nodeType, child);
                        int incrDecrMask = 0;
                        if (nodeType == Token.DEC)
                        {
                            incrDecrMask |= Node.DECR_FLAG;
                        }
                        if (post)
                        {
                            incrDecrMask |= Node.POST_FLAG;
                        }
                        n.putIntProp(Node.INCRDECR_PROP, incrDecrMask);
                        return n;
                    }
            }
            throw Kit.codeBug();
        }

        /// <summary>
        /// 创建一个调用和New节点
        /// </summary>
        /// <param name="nodeType"></param>
        /// <param name="child"></param>
        /// <returns></returns>
        public Node createCallOrNew(int nodeType, Node child)
        {
            int type = Node.NON_SPECIALCALL;
            if (child.getType() == Token.NAME)
            {
                String name = child.getString();
                if (name.Equals("eval"))
                {
                    type = Node.SPECIALCALL_EVAL;
                }
                else if (name.Equals("With"))
                {
                    type = Node.SPECIALCALL_WITH;
                }
            }
            else if (child.getType() == Token.GETPROP)
            {
                String name = child.getLastChild().getString();
                if (name.Equals("eval"))
                {
                    type = Node.SPECIALCALL_EVAL;
                }
            }
            Node node = new Node(nodeType, child);
            if (type != Node.NON_SPECIALCALL)
            {
                
                setRequiresActivation();
                node.putIntProp(Node.SPECIALCALL_PROP, type);
            }
            return node;
        }

        /// <summary>
        /// 创建Get属性
        /// </summary>
        /// <param name="target"></param>
        /// <param name="namespace1"></param>
        /// <param name="name"></param>
        /// <param name="memberTypeFlags"></param>
        /// <returns></returns>
        public Node createPropertyGet(Node target, String namespace1, String name,
                           int memberTypeFlags)
        {
            if (namespace1 == null && memberTypeFlags == 0)
            {
                if (target == null)
                {
                    return createName(name);
                }
                checkActivationName(name, Token.GETPROP);
                if (ScriptRuntime.isSpecialProperty(name))
                {
                    Node ref1 = new Node(Token.REF_SPECIAL, target);
                    ref1.putProp(Node.NAME_PROP, name);
                    return new Node(Token.GET_REF, ref1);
                }
                return new Node(Token.GETPROP, target, createString(name));
            }
            Node elem = createString(name);
            memberTypeFlags |= Node.PROPERTY_FLAG;
            return createMemberRefGet(target, namespace1, elem, memberTypeFlags);
        }

        private Node createMemberRefGet(Node target, String namespace1, Node elem,
                                    int memberTypeFlags)
        {
            Node nsNode = null;
            if (namespace1 != null)
            {
                // See 11.1.2 in ECMA 357
                if (namespace1 == "*")
                {
                    nsNode = new Node(Token.NULL);
                }
                else
                {
                    nsNode = createName(namespace1);
                }
            }
            Node ref1;
            if (target == null)
            {
                if (namespace1 == null)
                {
                    ref1 = new Node(Token.REF_NAME, elem);
                }
                else
                {
                    ref1 = new Node(Token.REF_NS_NAME, nsNode, elem);
                }
            }
            else
            {
                if (namespace1 == null)
                {
                    ref1 = new Node(Token.REF_MEMBER, target, elem);
                }
                else
                {
                    ref1 = new Node(Token.REF_NS_MEMBER, target, nsNode, elem);
                }
            }
            if (memberTypeFlags != 0)
            {
                ref1.putIntProp(Node.MEMBER_TYPE_PROP, memberTypeFlags);
            }
            return new Node(Token.GET_REF, ref1);
        }
        public Node createElementGet(Node target, String namespace1, Node elem,
                          int memberTypeFlags)
        {
            // OPT: could optimize to createPropertyGet
            // iff elem is string that can not be number
            if (namespace1 == null && memberTypeFlags == 0)
            {
                // stand-alone [aaa] as primary expression is array literal
                // declaration and should not come here!
                if (target == null) throw Kit.codeBug();
                return new Node(Token.GETELEM, target, elem);
            }
            return createMemberRefGet(target, namespace1, elem, memberTypeFlags);
        }

        /**
     * Given a destructuring assignment with a left hand side parsed
     * as an array or object literal and a right hand side expression,
     * rewrite as a series of assignments to the variables defined in
     * left from property accesses to the expression on the right.
     * @param type declaration type: Token.VAR or Token.LET or -1
     * @param left array or object literal containing NAME nodes for
     *        variables to assign
     * @param right expression to assign from
     * @return expression that performs a series of assignments to
     *         the variables defined in left
     */
        public Node createDestructuringAssignment(int type, Node left, Node right)
        {
            String tempName = parser.currentScriptOrFn.getNextTempName();
            Node result = destructuringAssignmentHelper(type, left, right,
                tempName);
            Node comma = result.getLastChild();
            comma.addChildToBack(createName(tempName));
            return result;
        }

        /// <summary>
        /// DOTQUERY 点语法
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="body"></param>
        /// <param name="lineno"></param>
        /// <returns></returns>
        public Node createDotQuery(Node obj, Node body, int lineno)
        {
            setRequiresActivation();
            Node result = new Node(Token.DOTQUERY, obj, body, lineno);
            return result;
        }

        //public Node createIncDec(int nodeType, bool post, Node child)
        //{
        //    child = makeReference(child);
        //    if (child == null)
        //    {
        //        String msg;
        //        if (nodeType == Token.DEC)
        //        {
        //            msg = "msg.bad.decr";
        //        }
        //        else
        //        {
        //            msg = "msg.bad.incr";
        //        }
        //        parser.reportError(msg);
        //        return null;
        //    }

        //    int childType = child.getType();

        //    switch (childType)
        //    {
        //        case Token.NAME:
        //        case Token.GETPROP:
        //        case Token.GETELEM:
        //        case Token.GET_REF:
        //            {
        //                Node n = new Node(nodeType, child);
        //                int incrDecrMask = 0;
        //                if (nodeType == Token.DEC)
        //                {
        //                    incrDecrMask |= Node.DECR_FLAG;
        //                }
        //                if (post)
        //                {
        //                    incrDecrMask |= Node.POST_FLAG;
        //                }
        //                n.putIntProp(Node.INCRDECR_PROP, incrDecrMask);
        //                return n;
        //            }
        //    }
        //    throw Kit.codeBug();
        //}

        /**
     * If statement
     */
        public Node createIf(Node cond, Node ifTrue, Node ifFalse, int lineno)
        {
            int condStatus = isAlwaysDefinedBoolean(cond);
            if (condStatus == ALWAYS_TRUE_BOOLEAN)
            {
                return ifTrue;
            }
            else if (condStatus == ALWAYS_FALSE_BOOLEAN)
            {
                if (ifFalse != null)
                {
                    return ifFalse;
                }
                // Replace if (false) xxx by empty block
                return new Node(Token.BLOCK, lineno);
            }

            Node result = new Node(Token.BLOCK, lineno);
            Node ifNotTarget = Node.newTarget();
            Node.Jump IFNE = new Node.Jump(Token.IFNE, cond);
            IFNE.target = ifNotTarget;

            result.addChildToBack(IFNE);
            result.addChildrenToBack(ifTrue);

            if (ifFalse != null)
            {
                Node endTarget = Node.newTarget();
                result.addChildToBack(makeJump(Token.GOTO, endTarget));
                result.addChildToBack(ifNotTarget);
                result.addChildrenToBack(ifFalse);
                result.addChildToBack(endTarget);
            }
            else
            {
                result.addChildToBack(ifNotTarget);
            }

            return result;
        }

        /**
     * If caseExpression argument is null it indicate default label.
     */
        public void addSwitchCase(Node switchBlock, Node caseExpression, Node statements)
        {
            if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
            Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild();
            if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();

            Node gotoTarget = Node.newTarget();
            if (caseExpression != null)
            {
                Node.Jump caseNode = new Node.Jump(Token.CASE, caseExpression);
                caseNode.target = gotoTarget;
                switchNode.addChildToBack(caseNode);
            }
            else
            {
                switchNode.setDefault(gotoTarget);
            }
            switchBlock.addChildToBack(gotoTarget);
            switchBlock.addChildToBack(statements);
        }

        public void closeSwitch(Node switchBlock)
        {
            if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
            Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild();
            if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();

            Node switchBreakTarget = Node.newTarget();
            // switchNode.target is only used by NodeTransformer
            // to detect switch end
            switchNode.target = switchBreakTarget;

            Node defaultTarget = switchNode.getDefault();
            if (defaultTarget == null)
            {
                defaultTarget = switchBreakTarget;
            }

            switchBlock.addChildAfter(makeJump(Token.GOTO, defaultTarget),
                                      switchNode);
            switchBlock.addChildToBack(switchBreakTarget);
        }




        /**
     * Statement leaf nodes.
     */

        public Node createSwitch(Node expr, int lineno)
        {
            //
            // The switch will be rewritten from:
            //
            // switch (expr) {
            //   case test1: statements1;
            //   ...
            //   default: statementsDefault;
            //   ...
            //   case testN: statementsN;
            // }
            //
            // to:
            //
            // {
            //     switch (expr) {
            //       case test1: goto label1;
            //       ...
            //       case testN: goto labelN;
            //     }
            //     goto labelDefault;
            //   label1:
            //     statements1;
            //   ...
            //   labelDefault:
            //     statementsDefault;
            //   ...
            //   labelN:
            //     statementsN;
            //   breakLabel:
            // }
            //
            // where inside switch each "break;" without label will be replaced
            // by "goto breakLabel".
            //
            // If the original switch does not have the default label, then
            // the transformed code would contain after the switch instead of
            //     goto labelDefault;
            // the following goto:
            //     goto breakLabel;
            //

            Node.Jump switchNode = new Node.Jump(Token.SWITCH, expr, lineno);
            Node block = new Node(Token.BLOCK, switchNode);
            return block;
        }

        /**
* Regular expressions
*/
        public Node createRegExp(int regexpIndex)
        {
            Node n = new Node(Token.REGEXP);
            n.putIntProp(Node.REGEXP_PROP, regexpIndex);
            return n;
        }

        /**
     * Label
     */
        public Node createLabel(int lineno)
        {
            return new Node.Jump(Token.LABEL, lineno);
        }

        /**
    * For .. In
    *
    */
        public Node createForIn(int declType, Node loop, Node lhs, Node obj, Node body,
                          bool isForEach)
        {
            int destructuring = -1;
            int destructuringLen = 0;
            Node lvalue;
            int type = lhs.getType();
            if (type == Token.VAR || type == Token.LET)
            {
                Node lastChild = lhs.getLastChild();
                if (lhs.getFirstChild() != lastChild)
                {
                    /*
                     * check that there was only one variable given.
                     * we can't do this in the parser, because then the
                     * parser would have to know something about the
                     * 'init' node of the for-in loop.
                     */
                    parser.reportError("msg.mult.index");
                }
                if (lastChild.getType() == Token.ARRAYLIT ||
                    lastChild.getType() == Token.OBJECTLIT)
                {
                    type = destructuring = lastChild.getType();
                    lvalue = lastChild;
                    destructuringLen = lastChild.getIntProp(
                        Node.DESTRUCTURING_ARRAY_LENGTH, 0);
                }
                else if (lastChild.getType() == Token.NAME)
                {
                    lvalue = Node.newString(Token.NAME, lastChild.getString());
                }
                else
                {
                    parser.reportError("msg.bad.for.in.lhs");
                    return obj;
                }
            }
            else if (type == Token.ARRAYLIT || type == Token.OBJECTLIT)
            {
                destructuring = type;
                lvalue = lhs;
                destructuringLen = lhs.getIntProp(Node.DESTRUCTURING_ARRAY_LENGTH, 0);
            }
            else
            {
                lvalue = makeReference(lhs);
                if (lvalue == null)
                {
                    parser.reportError("msg.bad.for.in.lhs");
                    return obj;
                }
            }

            Node localBlock = new Node(Token.LOCAL_BLOCK);
            int initType = (isForEach) ? Token.ENUM_INIT_VALUES :
                           (destructuring != -1) ? Token.ENUM_INIT_ARRAY :
                                                   Token.ENUM_INIT_KEYS;
            Node init = new Node(initType, obj);
            init.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
            Node cond = new Node(Token.ENUM_NEXT);
            cond.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
            Node id = new Node(Token.ENUM_ID);
            id.putProp(Node.LOCAL_BLOCK_PROP, localBlock);

            Node newBody = new Node(Token.BLOCK);
            Node assign;
            if (destructuring != -1)
            {
                assign = createDestructuringAssignment(declType, lvalue, id);
                if (!isForEach && (destructuring == Token.OBJECTLIT ||
                                   destructuringLen != 2))
                {
                    // destructuring assignment is only allowed in for..each or
                    // with an array type of length 2 (to hold key and value)
                    parser.reportError("msg.bad.for.in.destruct");
                }
            }
            else
            {
                assign = simpleAssignment(lvalue, id);
            }
            newBody.addChildToBack(new Node(Token.EXPR_VOID, assign));
            newBody.addChildToBack(body);

            loop = createWhile(loop, cond, newBody);
            loop.addChildToFront(init);
            if (type == Token.VAR || type == Token.LET)
                loop.addChildToFront(lhs);
            localBlock.addChildToBack(loop);

            return localBlock;
        }
        /// <summary>
        /// try/catch/finally
        /// </summary>
        /// <param name="varName"></param>
        /// <param name="catchCond"></param>
        /// <param name="stmts"></param>
        /// <param name="lineno"></param>
        /// <returns></returns>
        public Node createCatch(String varName, Node catchCond, Node stmts, int lineno)
        {
            if (catchCond == null)
            {
                catchCond = new Node(Token.EMPTY);
            }
            return new Node(Token.CATCH, createName(varName),
                            catchCond, stmts, lineno);
        }

        /// <summary>
        /// Throw
        /// </summary>
        /// <param name="expr"></param>
        /// <param name="lineno"></param>
        /// <returns></returns>
        public Node createThrow(Node expr, int lineno)
        {
            return new Node(Token.THROW, expr, lineno);
        }

        /**
     * Break (possibly labeled)
     */
        public Node createBreak(Node breakStatement, int lineno)
        {
            Node.Jump n = new Node.Jump(Token.BREAK, lineno);
            Node.Jump jumpStatement;
            int t = breakStatement.getType();
            if (t == Token.LOOP || t == Token.LABEL)
            {
                jumpStatement = (Node.Jump)breakStatement;
            }
            else if (t == Token.BLOCK
                       && breakStatement.getFirstChild().getType() == Token.SWITCH)
            {
                jumpStatement = (Node.Jump)breakStatement.getFirstChild();
            }
            else
            {
                throw Kit.codeBug();
            }
            n.setJumpStatement(jumpStatement);
            return n;
        }

        /**
         * Continue (possibly labeled)
         */
        public Node createContinue(Node loop, int lineno)
        {
            if (loop.getType() != Token.LOOP) Kit.codeBug();
            Node.Jump n = new Node.Jump(Token.CONTINUE, lineno);
            n.setJumpStatement((Node.Jump)loop);
            return n;
        }

        public Node createDefaultNamespace(Node expr, int lineno)
        {
            // default xml namespace requires activation
            setRequiresActivation();
            Node n = createUnary(Token.DEFAULTNAMESPACE, expr);
            Node result = createExprStatement(n, lineno);
            return result;
        }

        /// <summary>
        /// Debugger
        /// </summary>
        /// <param name="lineno"></param>
        /// <returns></returns>
        public Node createDebugger(int lineno)
        {
            return new Node(Token.DEBUGGER, lineno);
        }

        ///// <summary>
        ///// Label
        ///// </summary>
        ///// <param name="lineno"></param>
        ///// <returns></returns>
        //public Node createLabel(int lineno)
        //{
        //    return new Node.Jump(Token.LABEL, lineno);
        //}


        ///// <summary>
        ///// Return
        ///// </summary>
        ///// <param name="expr"></param>
        ///// <param name="lineno"></param>
        ///// <returns></returns>
        //public Node createReturn(Node expr, int lineno)
        //{
        //    return expr == null
        //        ? new Node(Token.RETURN, lineno)
        //        : new Node(Token.RETURN, expr, lineno);
        //}

        public Node createTryCatchFinally(Node tryBlock, Node catchBlocks,
                                 Node finallyBlock, int lineno)
        {
            bool hasFinally = (finallyBlock != null)
                                 && (finallyBlock.getType() != Token.BLOCK
                                     || finallyBlock.hasChildren());

            // short circuit
            if (tryBlock.getType() == Token.BLOCK && !tryBlock.hasChildren()
                && !hasFinally)
            {
                return tryBlock;
            }

            bool hasCatch = catchBlocks.hasChildren();

            // short circuit
            if (!hasFinally && !hasCatch)
            {
                // bc finally might be an empty block...
                return tryBlock;
            }


            Node handlerBlock = new Node(Token.LOCAL_BLOCK);
            Node.Jump pn = new Node.Jump(Token.TRY, tryBlock, lineno);
            pn.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);

            if (hasCatch)
            {
                // jump around catch code
                Node endCatch = Node.newTarget();
                pn.addChildToBack(makeJump(Token.GOTO, endCatch));

                // make a TARGET for the catch that the tcf node knows about
                Node catchTarget = Node.newTarget();
                pn.target = catchTarget;
                // mark it
                pn.addChildToBack(catchTarget);

                //
                //  Given
                //
                //   try {
                //       tryBlock;
                //   } catch (e if condition1) {
                //       something1;
                //   ...
                //
                //   } catch (e if conditionN) {
                //       somethingN;
                //   } catch (e) {
                //       somethingDefault;
                //   }
                //
                //  rewrite as
                //
                //   try {
                //       tryBlock;
                //       goto after_catch:
                //   } catch (x) {
                //       with (newCatchScope(e, x)) {
                //           if (condition1) {
                //               something1;
                //               goto after_catch;
                //           }
                //       }
                //   ...
                //       with (newCatchScope(e, x)) {
                //           if (conditionN) {
                //               somethingN;
                //               goto after_catch;
                //           }
                //       }
                //       with (newCatchScope(e, x)) {
                //           somethingDefault;
                //           goto after_catch;
                //       }
                //   }
                // after_catch:
                //
                // If there is no default catch, then the last with block
                // arround  "somethingDefault;" is replaced by "rethrow;"

                // It is assumed that catch handler generation will store
                // exeception object in handlerBlock register

                // Block with local for exception scope objects
                Node catchScopeBlock = new Node(Token.LOCAL_BLOCK);

                // expects catchblocks children to be (cond block) pairs.
                Node cb = catchBlocks.getFirstChild();
                bool hasDefault = false;
                int scopeIndex = 0;
                while (cb != null)
                {
                    int catchLineNo = cb.getLineno();

                    Node name = cb.getFirstChild();
                    Node cond = name.getNext();
                    Node catchStatement = cond.getNext();
                    cb.removeChild(name);
                    cb.removeChild(cond);
                    cb.removeChild(catchStatement);

                    // Add goto to the catch statement to jump out of catch
                    // but prefix it with LEAVEWITH since try..catch produces
                    // "with"code in order to limit the scope of the exception
                    // object.
                    catchStatement.addChildToBack(new Node(Token.LEAVEWITH));
                    catchStatement.addChildToBack(makeJump(Token.GOTO, endCatch));

                    // Create condition "if" when present
                    Node condStmt;
                    if (cond.getType() == Token.EMPTY)
                    {
                        condStmt = catchStatement;
                        hasDefault = true;
                    }
                    else
                    {
                        condStmt = createIf(cond, catchStatement, null,
                                            catchLineNo);
                    }

                    // Generate code to create the scope object and store
                    // it in catchScopeBlock register
                    Node catchScope = new Node(Token.CATCH_SCOPE, name,
                                               createUseLocal(handlerBlock));
                    catchScope.putProp(Node.LOCAL_BLOCK_PROP, catchScopeBlock);
                    catchScope.putIntProp(Node.CATCH_SCOPE_PROP, scopeIndex);
                    catchScopeBlock.addChildToBack(catchScope);

                    // Add with statement based on catch scope object
                    catchScopeBlock.addChildToBack(
                        createWith(createUseLocal(catchScopeBlock), condStmt,
                                   catchLineNo));

                    // move to next cb
                    cb = cb.getNext();
                    ++scopeIndex;
                }
                pn.addChildToBack(catchScopeBlock);
                if (!hasDefault)
                {
                    // Generate code to rethrow if no catch clause was executed
                    Node rethrow = new Node(Token.RETHROW);
                    rethrow.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
                    pn.addChildToBack(rethrow);
                }

                pn.addChildToBack(endCatch);
            }

            if (hasFinally)
            {
                Node finallyTarget = Node.newTarget();
                pn.setFinally(finallyTarget);

                // add jsr finally to the try block
                pn.addChildToBack(makeJump(Token.JSR, finallyTarget));

                // jump around finally code
                Node finallyEnd = Node.newTarget();
                pn.addChildToBack(makeJump(Token.GOTO, finallyEnd));

                pn.addChildToBack(finallyTarget);
                Node fBlock = new Node(Token.FINALLY, finallyBlock);
                fBlock.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
                pn.addChildToBack(fBlock);

                pn.addChildToBack(finallyEnd);
            }
            handlerBlock.addChildToBack(pn);
            return handlerBlock;
        }

        /// <summary>
        /// With
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="body"></param>
        /// <param name="lineno"></param>
        /// <returns></returns>
        public Node createWith(Node obj, Node body, int lineno)
        {
            setRequiresActivation();
            Node result = new Node(Token.BLOCK, lineno);
            result.addChildToBack(new Node(Token.ENTERWITH, obj));
            Node bodyNode = new Node(Token.WITH, body, lineno);
            result.addChildrenToBack(bodyNode);
            result.addChildToBack(new Node(Token.LEAVEWITH));
            return result;
        }

        public Node createUseLocal(Node localBlock)
        {
            if (Token.LOCAL_BLOCK != localBlock.getType()) throw Kit.codeBug();
            Node result = new Node(Token.LOCAL_LOAD);
            result.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
            return result;
        }

        /// <summary>
        /// 创建for语句
        /// </summary>
        /// <param name="loop"></param>
        /// <param name="init"></param>
        /// <param name="test"></param>
        /// <param name="incr"></param>
        /// <param name="body"></param>
        /// <returns></returns>
        public Node createFor(Node loop, Node init, Node test, Node incr, Node body)
        {
            if (init.getType() == Token.LET)
            {
                // rewrite "for (let i=s; i < N; i++)..." as 
                // "let (i=s) { for (; i < N; i++)..." so that "s" is evaluated
                // outside the scope of the for.
                Node.Scope let = Node.Scope.splitScope((Node.Scope)loop);
                let.setType(Token.LET);
                let.addChildrenToBack(init);
                let.addChildToBack(createLoop((Node.Jump)loop, LOOP_FOR, body, test,
                    new Node(Token.EMPTY), incr));
                return let;
            }
            return createLoop((Node.Jump)loop, LOOP_FOR, body, test, init, incr);
        }

        private Node.Jump makeJump(int type, Node target)
        {
            Node.Jump n = new Node.Jump(type);
            n.target = target;
            return n;
        }

        /**
     * While
     */
        public Node createWhile(Node loop, Node cond, Node body)
        {
            return createLoop((Node.Jump)loop, LOOP_WHILE, body, cond,
                              null, null);
        }

        /**
         * DoWhile
         */
        public Node createDoWhile(Node loop, Node body, Node cond)
        {
            return createLoop((Node.Jump)loop, LOOP_DO_WHILE, body, cond,
                              null, null);
        }

        private Node createLoop(Node.Jump loop, int loopType, Node body, Node cond,
                            Node init, Node incr)
        {
            Node bodyTarget = Node.newTarget();
            Node condTarget = Node.newTarget();
            if (loopType == LOOP_FOR && cond.getType() == Token.EMPTY)
            {
                cond = new Node(Token.TRUE);
            }
            Node.Jump IFEQ = new Node.Jump(Token.IFEQ, cond);
            IFEQ.target = bodyTarget;
            Node breakTarget = Node.newTarget();

            loop.addChildToBack(bodyTarget);
            loop.addChildrenToBack(body);
            if (loopType == LOOP_WHILE || loopType == LOOP_FOR)
            {
                // propagate lineno to condition
                loop.addChildrenToBack(new Node(Token.EMPTY, loop.getLineno()));
            }
            loop.addChildToBack(condTarget);
            loop.addChildToBack(IFEQ);
            loop.addChildToBack(breakTarget);

            loop.target = breakTarget;
            Node continueTarget = condTarget;

            if (loopType == LOOP_WHILE || loopType == LOOP_FOR)
            {
                // Just add a GOTO to the condition in the do..while
                loop.addChildToFront(makeJump(Token.GOTO, condTarget));

                if (loopType == LOOP_FOR)
                {
                    int initType = init.getType();
                    if (initType != Token.EMPTY)
                    {
                        if (initType != Token.VAR && initType != Token.LET)
                        {
                            init = new Node(Token.EXPR_VOID, init);
                        }
                        loop.addChildToFront(init);
                    }
                    Node incrTarget = Node.newTarget();
                    loop.addChildAfter(incrTarget, body);
                    if (incr.getType() != Token.EMPTY)
                    {
                        incr = new Node(Token.EXPR_VOID, incr);
                        loop.addChildAfter(incr, incrTarget);
                    }
                    continueTarget = incrTarget;
                }
            }

            loop.setContinue(continueTarget);

            return loop;
        }

        public Node getLabelLoop(Node label)
        {
            return ((Node.Jump)label).getLoop();
        }

        /// <summary>
        /// Label
        /// </summary>
        /// <param name="labelArg"></param>
        /// <param name="statement"></param>
        /// <returns></returns>
        public Node createLabeledStatement(Node labelArg, Node statement)
        {
            Node.Jump label = (Node.Jump)labelArg;

            // Make a target and put it _after_ the statement
            // node.  And in the LABEL node, so breaks get the
            // right target.

            Node breakTarget = Node.newTarget();
            Node block = new Node(Token.BLOCK, label, statement, breakTarget);
            label.target = breakTarget;

            return block;
        }

        /**
     * Object Literals
     * <BR> createObjectLiteral rewrites its argument as object
     * creation plus object property entries, so later compiler
     * stages don't need to know about object literals.
     */
        public Node createObjectLiteral(ObjArray elems)
        {
            int size = elems.size() / 2;
            Node object1 = new Node(Token.OBJECTLIT);
            Object[] properties;
            if (size == 0)
            {
                properties = ScriptRuntime.emptyArgs;
            }
            else
            {
                properties = new Object[size];
                for (int i = 0; i != size; ++i)
                {
                    properties[i] = elems.get(2 * i);
                    Node value = (Node)elems.get(2 * i + 1);
                    object1.addChildToBack(value);
                }
            }
            object1.putProp(Node.OBJECT_IDS_PROP, properties);
            return object1;
        }

        /// <summary>
        /// 创建表达式语句节点
        /// </summary>
        /// <param name="expr"></param>
        /// <param name="lineno"></param>
        /// <returns></returns>
        public Node createExprStatement(Node expr, int lineno)
        {
            int type;
            if (parser.insideFunction())
            {
                type = Token.EXPR_VOID;
            }
            else
            {
                type = Token.EXPR_RESULT;
            }
            return new Node(type, expr, lineno);
        }

        public Node createArrayLiteral(ObjArray elems, int skipCount, int destructuringLen)
        {
            int length = elems.size();
            int[] skipIndexes = null;
            if (skipCount != 0)
            {
                skipIndexes = new int[skipCount];
            }
            Node array = new Node(Token.ARRAYLIT);
            for (int i = 0, j = 0; i != length; ++i)
            {
                Node elem = (Node)elems.get(i);
                if (elem != null)
                {
                    array.addChildToBack(elem);
                }
                else
                {
                    skipIndexes[j] = i;
                    ++j;
                }
            }
            if (skipCount != 0)
            {
                array.putProp(Node.SKIP_INDEXES_PROP, skipIndexes);
            }
            array.putIntProp(Node.DESTRUCTURING_ARRAY_LENGTH, destructuringLen);
            return array;
        }

        /**
    * Create loop node. The parser will later call
    * createWhile|createDoWhile|createFor|createForIn
    * to finish loop generation.
    */
        public Node createLoopNode(Node loopLabel, int lineno)
        {
            Node.Jump result = new Node.Scope(Token.LOOP, lineno);
            if (loopLabel != null)
            {
                ((Node.Jump)loopLabel).setLoop(result);
            }
            return result;
        }

        /**
     * String (for literals)
     */
        public Node createString(String _string)
        {
            return Node.newString(_string);
        }

        /**
         * Number (for literals)
         */
        public Node createNumber(double number)
        {
            return Node.newNumber(number);
        }

        /**
         * Name
         */
        public Node createName(String name)
        {
            checkActivationName(name, Token.NAME);
            return Node.newString(Token.NAME, name);
        }

        public Node createCondExpr(Node cond, Node ifTrue, Node ifFalse)
        {
            int condStatus = isAlwaysDefinedBoolean(cond);
            if (condStatus == ALWAYS_TRUE_BOOLEAN)
            {
                return ifTrue;
            }
            else if (condStatus == ALWAYS_FALSE_BOOLEAN)
            {
                return ifFalse;
            }
            return new Node(Token.HOOK, cond, ifTrue, ifFalse);
        }

        /// <summary>
        ///  语句块 创建一个空块  必须后续调用语句添加到节点
        /// </summary>
        /// <param name="lineno"></param>
        /// <returns></returns>
        public Node createBlock(int lineno)
        {
            return new Node(Token.BLOCK, lineno);
        }
        public Node initFunction(FunctionNode fnNode, int functionIndex,
                      Node statements, int functionType)
        {
            fnNode.itsFunctionType = functionType;
            fnNode.addChildToBack(statements);

            int functionCount = fnNode.getFunctionCount();
            if (functionCount != 0)
            {
                // Functions containing other functions require activation objects
                fnNode.itsNeedsActivation = true;
            }

            if (functionType == FunctionNode.FUNCTION_EXPRESSION)
            {
                String name = fnNode.getFunctionName();
                if (name != null && name.Length != 0)
                {
                    // A function expression needs to have its name as a
                    // variable (if it isn't already allocated as a variable).
                    // See ECMA Ch. 13.  We add code to the beginning of the
                    // function to initialize a local variable of the
                    // function's name to the function value.
                    Node setFn = new Node(Token.EXPR_VOID,
                                     new Node(Token.SETNAME,
                                         Node.newString(Token.BINDNAME, name),
                                         new Node(Token.THISFN)));
                    statements.addChildrenToFront(setFn);
                }
            }

            // Add return to end if needed.
            Node lastStmt = statements.getLastChild();
            if (lastStmt == null || lastStmt.getType() != Token.RETURN)
            {
                statements.addChildToBack(new Node(Token.RETURN));
            }

            Node result = Node.newString(Token.FUNCTION,
                                         fnNode.getFunctionName());
            result.putIntProp(Node.FUNCTION_PROP, functionIndex);
            return result;
        }

        public Node createExprStatementNoReturn(Node expr, int lineno)
        {
            return new Node(Token.EXPR_VOID, expr, lineno);
        }

        /**
     * Create a node that can be used to hold lexically scoped variable
     * definitions (via let declarations).
     * 
     * @param token the token of the node to create
     * @param lineno line number of source
     * @return the created node
     */
        public Node createScopeNode(int token, int lineno)
        {
            return new Node.Scope(token, lineno);
        }

        private Node createName(int type, String name, Node child)
        {
            Node result = createName(name);
            result.setType(type);
            if (child != null)
                result.addChildToBack(child);
            return result;
        }

        private Node destructuringAssignmentHelper(int variableType, Node left,
                                               Node right, String tempName)
        {
            Node result = createScopeNode(Token.LETEXPR,
                parser.getCurrentLineNumber());
            result.addChildToFront(new Node(Token.LET,
                createName(Token.NAME, tempName, right)));
            try
            {
                parser.pushScope(result);
                parser.defineSymbol(Token.LET, true, tempName);
            }
            finally
            {
                parser.popScope();
            }
            Node comma = new Node(Token.COMMA);
            result.addChildToBack(comma);
            int setOp = variableType == Token.CONST ? Token.SETCONST
                                                         : Token.SETNAME;
            List<String> destructuringNames = new List<String>();
            bool empty = true;
            int type = left.getType();
            if (type == Token.ARRAYLIT)
            {
                int index = 0;
                int[] skipIndices = (int[])left.getProp(Node.SKIP_INDEXES_PROP);
                int skip = 0;
                Node n = left.getFirstChild();
                for (; ; )
                {
                    if (skipIndices != null)
                    {
                        while (skip < skipIndices.Length &&
                               skipIndices[skip] == index)
                        {
                            skip++;
                            index++;
                        }
                    }
                    if (n == null)
                        break;
                    Node rightElem = new Node(Token.GETELEM,
                        createName(tempName),
                        createNumber(index));
                    if (n.getType() == Token.NAME)
                    {
                        String name = n.getString();
                        comma.addChildToBack(new Node(setOp,
                            createName(Token.BINDNAME, name, null),
                            rightElem));
                        if (variableType != -1)
                        {
                            parser.defineSymbol(variableType, true, name);
                            destructuringNames.Add(name);
                        }
                    }
                    else
                    {
                        comma.addChildToBack(
                            destructuringAssignmentHelper(variableType, n,
                                rightElem,
                                parser.currentScriptOrFn.getNextTempName()));
                    }
                    index++;
                    empty = false;
                    n = n.getNext();
                }
            }
            else if (type == Token.OBJECTLIT)
            {
                int index = 0;
                Object[] propertyIds = (Object[])
                    left.getProp(Node.OBJECT_IDS_PROP);
                for (Node n = left.getFirstChild(); n != null; n = n.getNext())
                {
                    Object id = propertyIds[index];
                    Node rightElem = (id.GetType() == typeof(String))
                        ? new Node(Token.GETPROP,
                            createName(tempName),
                            createString((String)id))
                        : new Node(Token.GETELEM,
                            createName(tempName),
                            createNumber(((double)id)));
                    if (n.getType() == Token.NAME)
                    {
                        String name = n.getString();
                        comma.addChildToBack(new Node(setOp,
                            createName(Token.BINDNAME, name, null),
                            rightElem));
                        if (variableType != -1)
                        {
                            parser.defineSymbol(variableType, true, name);
                            destructuringNames.Add(name);
                        }
                    }
                    else
                    {
                        comma.addChildToBack(
                            destructuringAssignmentHelper(variableType, n,
                                rightElem,
                                parser.currentScriptOrFn.getNextTempName()));
                    }
                    index++;
                    empty = false;
                }
            }
            else if (type == Token.GETPROP || type == Token.GETELEM)
            {
                comma.addChildToBack(simpleAssignment(left, createName(tempName)));
            }
            else
            {
                parser.reportError("msg.bad.assign.left");
            }
            if (empty)
            {
                // Don't want a COMMA node with no children. Just add a zero.
                comma.addChildToBack(createNumber(0));
            }
            result.putProp(Node.DESTRUCTURING_NAMES, destructuringNames);
            return result;
        }

        public Node createAssignment(int assignType, Node left, Node right)
        {
            Node ref1 = makeReference(left);
            if (ref1 == null)
            {
                if (left.getType() == Token.ARRAYLIT ||
                    left.getType() == Token.OBJECTLIT)
                {
                    if (assignType != Token.ASSIGN)
                    {
                        parser.reportError("msg.bad.destruct.op");
                        return right;
                    }
                    return createDestructuringAssignment(-1, left, right);
                }
                parser.reportError("msg.bad.assign.left");
                return right;
            }
            left = ref1;

            int assignOp;
            switch (assignType)
            {
                case Token.ASSIGN:
                    return simpleAssignment(left, right);
                case Token.ASSIGN_BITOR: assignOp = Token.BITOR; break;
                case Token.ASSIGN_BITXOR: assignOp = Token.BITXOR; break;
                case Token.ASSIGN_BITAND: assignOp = Token.BITAND; break;
                case Token.ASSIGN_LSH: assignOp = Token.LSH; break;
                case Token.ASSIGN_RSH: assignOp = Token.RSH; break;
                case Token.ASSIGN_URSH: assignOp = Token.URSH; break;
                case Token.ASSIGN_ADD: assignOp = Token.ADD; break;
                case Token.ASSIGN_SUB: assignOp = Token.SUB; break;
                case Token.ASSIGN_MUL: assignOp = Token.MUL; break;
                case Token.ASSIGN_DIV: assignOp = Token.DIV; break;
                case Token.ASSIGN_MOD: assignOp = Token.MOD; break;
                default: throw Kit.codeBug();
            }

            int nodeType = left.getType();
            switch (nodeType)
            {
                case Token.NAME:
                    {
                        Node op = new Node(assignOp, left, right);
                        Node lvalueLeft = Node.newString(Token.BINDNAME, left.getString());
                        return new Node(Token.SETNAME, lvalueLeft, op);
                    }
                case Token.GETPROP:
                case Token.GETELEM:
                    {
                        Node obj = left.getFirstChild();
                        Node id = left.getLastChild();

                        int type = nodeType == Token.GETPROP
                                   ? Token.SETPROP_OP
                                   : Token.SETELEM_OP;

                        Node opLeft = new Node(Token.USE_STACK);
                        Node op = new Node(assignOp, opLeft, right);
                        return new Node(type, obj, id, op);
                    }
                case Token.GET_REF:
                    {
                        ref1 = left.getFirstChild();
                        checkMutableReference(ref1);
                        Node opLeft = new Node(Token.USE_STACK);
                        Node op = new Node(assignOp, opLeft, right);
                        return new Node(Token.SET_REF_OP, ref1, op);
                    }
            }

            throw Kit.codeBug();
        }

        public FunctionNode createFunction(String name)
        {
            return new FunctionNode(name);
        }

        private void checkMutableReference(Node n)
        {
            int memberTypeFlags = n.getIntProp(Node.MEMBER_TYPE_PROP, 0);
            if ((memberTypeFlags & Node.DESCENDANTS_FLAG) != 0)
            {
                parser.reportError("msg.bad.assign.left");
            }
        }

        private Node simpleAssignment(Node left, Node right)
        {
            int nodeType = left.getType();
            switch (nodeType)
            {
                case Token.NAME:
                    left.setType(Token.BINDNAME);
                    return new Node(Token.SETNAME, left, right);

                case Token.GETPROP:
                case Token.GETELEM:
                    {
                        Node obj = left.getFirstChild();
                        Node id = left.getLastChild();
                        int type;
                        if (nodeType == Token.GETPROP)
                        {
                            type = Token.SETPROP;
                        }
                        else
                        {
                            type = Token.SETELEM;
                        }
                        return new Node(type, obj, id, right);
                    }
                case Token.GET_REF:
                    {
                        Node ref1 = left.getFirstChild();
                        checkMutableReference(ref1);
                        return new Node(Token.SET_REF, ref1, right);
                    }
            }

            throw Kit.codeBug();
        }

        /// <summary>
        /// 核对方法名字
        /// </summary>
        /// <param name="name"></param>
        /// <param name="token"></param>
        private void checkActivationName(String name, int token)
        {
            if (parser.insideFunction())
            {
                bool activation = false;
                if ("arguments".Equals(name)
                    || (parser.compilerEnv.activationNames != null
                        && parser.compilerEnv.activationNames.Contains(name)))
                {
                    activation = true;
                }
                else if ("length".Equals(name))
                {
                    if (token == Token.GETPROP
                        && parser.compilerEnv.getLanguageVersion()
                           == Context.VERSION_1_2)
                    {
                        // Use of "length" in 1.2 requires an activation object.
                        activation = true;
                    }
                }
                if (activation)
                {
                    setRequiresActivation();
                }
            }
        }

        private static int LOOP_DO_WHILE = 0;
        private static int LOOP_WHILE = 1;
        private static int LOOP_FOR = 2;

        private static int ALWAYS_TRUE_BOOLEAN = 1;
        private static int ALWAYS_FALSE_BOOLEAN = -1;
        private Parser parser;
    }
}
